/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.orhanobut.hawk;

import java.nio.charset.Charset;

/**
 * 加密算法
 *
 * @since 2021-05-26
 */
public class Rc4 {
    private static final int NUM = 256;
    private static final int NUM1 = 0xff;
    private static final int NUM2 = 2;
    private static final int NUM3 = 4;
    /**
     * 构造方法
     */

    public void rc4() {
    }

    /**
     * 加密*
     *
     * @param data byte[]加密的数据
     * @param key key 加密的密令
     * @return String
     */
    public static String decryRc4(byte[] data, String key) {
        if (data == null || key == null) {
            return "";
        }
        return asString(rc4Base(data, key));
    }

    /**
     * 加密
     *
     * @param data 加密的数据
     * @param key key 加密的密令
     * @return String
     */
    public static String decryRc4(String data, String key) {
        if (data == null || key == null) {
            return "";
        }
        return new String(rc4Base(hexString2Bytes(data), key));
    }

    /**
     * 解密
     *
     * @param data 解密的数据
     * @param key key 加密的密令
     * @return byte
     */

    public static byte[] encryRc4Byte(String data, String key) {
        if (data == null || key == null) {
            return new byte[]{};
        }
        byte[] mData = data.getBytes(Charset.defaultCharset());
        return rc4Base(mData, key);
    }

    /**
     * 解密
     *
     * @param data 解密的数据
     * @param key key 加密的密令
     * @return String
     */
    public static String encryRc4String(String data, String key) {
        if (data == null || key == null) {
            return "";
        }
        return toHexString(asString(encryRc4Byte(data, key)));
    }

    /**
     * 转换
     *
     * @param buf buf
     * @return String
     */
    private static String asString(byte[] buf) {
        StringBuffer strbuf = new StringBuffer(buf.length);
        for (int ii = 0; ii < buf.length; ii++) {
            strbuf.append((char) buf[ii]);
        }
        return strbuf.toString();
    }

    private static byte[] initKey(String key) {
        byte[] mKey = key.getBytes(Charset.defaultCharset());
        byte[] state = new byte[NUM];

        for (int ii = 0; ii < NUM; ii++) {
            state[ii] = (byte) ii;
        }
        int index1 = 0;
        int index2 = 0;
        if (mKey == null || mKey.length == 0) {
            return new byte[]{};
        }
        for (int ii = 0; ii < NUM; ii++) {
            index2 = ((mKey[index1] & NUM1) + (state[ii] & NUM1) + index2) & NUM1;
            byte tmp = state[ii];
            state[ii] = state[index2];
            state[index2] = tmp;
            index1 = (index1 + 1) % mKey.length;
        }
        return state;
    }

    private static String toHexString(String s) {
        StringBuffer str = new StringBuffer();
        for (int ii = 0; ii < s.length(); ii++) {
            int ch = (int) s.charAt(ii);
            String s4 = Integer.toHexString(ch & NUM1);
            if (s4.length() == 1) {
                s4 = '0' + s4;
            }

            str = str.append(s4);
        }
        return str.toString(); // 0x表示十六进制
    }

    private static byte[] hexString2Bytes(String src) {
        int size = src.length();
        byte[] ret = new byte[size / NUM2];
        byte[] tmp = src.getBytes(Charset.defaultCharset());
        for (int ii = 0; ii < size / NUM2; ii++) {
            ret[ii] = uniteBytes(tmp[ii * NUM2], tmp[ii * NUM2 + 1]);
        }
        return ret;
    }

    private static byte uniteBytes(byte src0, byte src1) {
        char b0 = (char) Byte.decode("0x" + new String(new byte[]{src0})).byteValue();
        b0 = (char) (b0 << NUM3);
        char b1 = (char) Byte.decode("0x" + new String(new byte[]{src1})).byteValue();
        byte ret = (byte) (b0 ^ b1);
        return ret;
    }

    private static byte[] rc4Base(byte[] input, String mkey) {
        int x1 = 0;
        int y1 = 0;
        byte[] key = initKey(mkey);
        int xorIndex;
        byte[] result = new byte[input.length];

        for (int ii = 0; ii < input.length; ii++) {
            x1 = (x1 + 1) & NUM1;
            y1 = ((key[x1] & NUM1) + y1) & NUM1;
            byte tmp = key[x1];
            key[x1] = key[y1];
            key[y1] = tmp;
            xorIndex = ((key[x1] & NUM1) + (key[y1] & NUM1)) & NUM1;
            result[ii] = (byte) (input[ii] ^ key[xorIndex]);
        }
        return result;
    }
}
